#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <cerrno>
#include <unordered_map>
#include <functional>
#include "Sock.hpp"
#include "Epoller.hpp"
#include "Log.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

class Connection;
class TcpServer;

using func_t = std::function<int(Connection *)>;
using callback_t = std::function<int(Connection *, std::string &)>;

class Connection
{
public:
    int sock_;
    TcpServer *R_;

    std::string inbuffer_;
    std::string outbuffer_;

    func_t recver_;
    func_t sender_;
    func_t excepter_;
public:
    Connection(int sock, TcpServer *r) : sock_(sock), R_(r) {}

    void SetRecver(func_t recver) { recver_ = recver; }
    void SetSender(func_t sender) { sender_ = sender; }
    void SetExcepter(func_t excepter) { excepter_ = excepter; }

    ~Connection() {}
};

class TcpServer
{
public:
    TcpServer(callback_t cb, int port = 8081) : cb_(cb)
    {
        revs_ = new struct epoll_event[revs_num];

        listensock_ = Sock::Socket();
        Util::SetNonBlock(listensock_);
        Sock::Bind(listensock_, port);
        Sock::Listen(listensock_);

        epfd_ = Epoller::CreateEpoller();

        AddConnection(listensock_, EPOLLIN | EPOLLET,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
    }

    void AddConnection(int sockfd, uint32_t event, func_t recver, func_t sender, func_t excepter)
    {
        if(event & EPOLLET) Util::SetNonBlock(sockfd);

        Epoller::AddEvent(epfd_, sockfd, event);

        Connection *conn = new Connection(sockfd, this);
        conn->SetRecver(recver);
        conn->SetSender(sender);
        conn->SetExcepter(excepter);

        connections_.insert(std::make_pair(sockfd, conn));
        logMessage(DEBUG, "添加新链接到connections成功: %d", sockfd);
    }

    int Accepter(Connection *conn)
    {
        while(true)
        {
            std::string clientip;
            uint16_t clientport = 0;
            int sockfd = Sock::Accept(conn->sock_, &clientip, &clientport);
            if(sockfd < 0)
            {
                if(errno == EINTR) continue;
                else if(errno == EAGAIN || errno == EWOULDBLOCK) break;
                else
                {
                    logMessage(WARNING, "accept error");
                    return -1;
                }
            }
            logMessage(DEBUG, "get a new link: %d", sockfd);

            AddConnection(sockfd, EPOLLIN | EPOLLET,
                          std::bind(&TcpServer::TcpRecver, this, std::placeholders::_1),
                          std::bind(&TcpServer::TcpSender, this, std::placeholders::_1),
                          std::bind(&TcpServer::TcpExcepter, this, std::placeholders::_1));
        }
        return 0;
    }

    int TcpRecver(Connection *conn)
    {
        while(true)
        {
            char buffer[1024];
            ssize_t s = recv(conn->sock_, buffer, sizeof(buffer) - 1, 0);
            std::cout << s << std::endl;
            //logMessage(DEBUG, "s = %d", s);
            if(s > 0)
            {
                buffer[s] = 0;
                conn->inbuffer_ += buffer;
                //logMessage(DEBUG, "此时的inbuffer: %s", conn->inbuffer_);
            }
            else if(s == 0)
            {
                logMessage(DEBUG, "client quit");
                conn->excepter_(conn);
                break;
            }
            else
            {
                std::cout << errno << ' ' << strerror(errno) << std::endl;
                if(errno == EINTR) continue;
                else if(errno == EAGAIN || errno == EWOULDBLOCK) break;
                else
                {
                    logMessage(DEBUG, "recv error: %d:%s", errno, strerror(errno));
                    conn->excepter_(conn);
                    break;
                }
            }
        }

        std::vector<std::string> result;
        PackageSplit(conn->inbuffer_, &result);
        for(auto &message : result)
        {
            cb_(conn, message);
        }
        return 0;
    }

    int TcpSender(Connection *conn)
    {
        while(true)
        {
            ssize_t n = send(conn->sock_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
            if(n > 0)
            {
                conn->outbuffer_.erase(0, n);
            }
            else
            {
                if(errno == EINTR) continue;
                else if(errno == EAGAIN || errno == EWOULDBLOCK) break;
                else
                {
                    conn->excepter_(conn);
                    logMessage(DEBUG, "send error: %d:%s", errno, strerror(errno));
                    break;
                }
            }
        }

        return 0;
    }

    int TcpExcepter(Connection *conn)
    {
        if(!IsExists(conn->sock_)) return -1;

        Epoller::DelEvent(epfd_, conn->sock_);
        logMessage(DEBUG, "remove epoll event!");

        close(conn->sock_);
        logMessage(DEBUG, "close fd: %d", conn->sock_);

        delete connections_[conn->sock_];
        logMessage(DEBUG, "delete connection object done");

        connections_.erase(conn->sock_);
        logMessage(DEBUG, "erase connection from connections");

        return 0;
    }

    bool IsExists(int sock)
    {
        auto iter = connections_.find(sock);
        if(iter == connections_.end()) return false;
        else return true;
    }

    void EnableReadWrite(int sock, bool readable, bool writeable)
    {
        uint32_t event = 0;
        event |= (readable ? EPOLLIN : 0);
        event |= (writeable ? EPOLLOUT : 0);
        Epoller::ModEvent(epfd_, sock, event);
    }

    void Dispatcher()
    {
        int n = Epoller::LoopOnce(epfd_, revs_, revs_num);
        for(int i = 0; i < n; ++i)
        {
            int sock = revs_[i].data.fd;
            uint32_t revent = revs_[i].events;
            if(revent & EPOLLHUP) revent |= (EPOLLIN | EPOLLOUT);
            if(revent & EPOLLERR) revent |= (EPOLLIN | EPOLLOUT);

            if(revent & EPOLLIN)
            {
                if(IsExists(sock) && connections_[sock]->recver_)
                    connections_[sock]->recver_(connections_[sock]);
            }
            if (revent & EPOLLOUT)
            {
                if (IsExists(sock) && connections_[sock]->sender_)
                    connections_[sock]->sender_(connections_[sock]);
            }
        }
    }

    void Run()
    {
        while(true)
        {
            Dispatcher();
        }
    }

    ~TcpServer()
    {
        if(listensock_ != -1) close(listensock_);
        if(epfd_ != -1) close(epfd_);
        delete[] revs_;
    }
private:
    static const int revs_num = 64;
    int listensock_;
    int epfd_;
    std::unordered_map<int, Connection *> connections_;
    struct epoll_event *revs_;
    callback_t cb_;
};